<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<script>
    "use script"; 
    console.log("这是严格模式。")
</script>
<script>
    console.log("这是正常模式。")
    window.onload = function () {
        // 定义父类 共享属性 最好不在这里定义方法  会在每一次调用次构造函数时绑定this的方法 影响性能
        function ParentObj(name) {
            this.name = name;
            this.colors = ['red', 'black'];
        }

        // 定义子类 重写覆盖父类属性 以及 定义私有属性
        function ChildrenObj(name, age) {
            ParentObj.call(this, name); //继承了父类的属性和方法  其实就是重写了与父类相同的属性和方法 以覆盖父类的属性方法
            this.age = age;
        }

        // 定义子类 重写覆盖父类属性 以及 定义私有属性
        function ChildrenObj2(name, age, interest) {
            ParentObj.call(this, name);
            this.age = age;
            this.interest = interest;
        }
        
        // 定义父类共享的方法 最好不要直接在ParentObj构造函数内定义  理由如上
        ParentObj.prototype.sayName = function () {
            console.log(this.name);
        }

        ParentObj.prototype.sayAge = function () {
            console.log(this.age);
        }

        /**
         *  此函数接受两个参数 : 子类型的构造函数 , 超类型的构造函数 
         *  第一步创建父类的原型的一个副本
         *  第二步是为创建的副本添加constructor属性,从而弥补因重写原型而丢失的constructor属性
         *  最后一步 将创建的对象(即副本)赋值给子类型的原型 
         *  因此在子类型只在实例化中存在父类型的属性,在原型中不会再次有相同的属性
         *  因为子类型的原型不再是指向父类,而是指向父类的原型
         */
        function inheritPrototype(child, parent) {
            let parentPrototype = Object.create(parent.prototype);
            parentPrototype.constructor = child;
            child.prototype = parentPrototype;
        }

        inheritPrototype(ChildrenObj, ParentObj);

        // 实例化子类
        var c1 = new ChildrenObj("children", "22");

        inheritPrototype(ChildrenObj2, ParentObj);

        // 实例化子类
        var c2 = new ChildrenObj2("children2", "23", "听音乐");
        
        // 在c1中修改属性 不会影响到c2 虽然属性名一样 但构造函数已经不同 时两个不同作用域的属性
        // 从而避免了原先共享属性的问题 
        c1.colors.push("yellow");

        console.log(c1);
        console.log(c2);

        c1.sayAge();
        c2.sayAge();

        console.log(c1.sayAge === c2.sayAge); //true;
        
        // 同理 不同子类同样可以为自己添加私有的属性或方法 
        c1.sayAll = function () {
            console.log(this.name + ',' + this.age);
        }

        console.log(c1);
        console.log(c2);

        c1.sayAll(); //正常输出
        c2.sayAll(); //报错 c2并没有此方法 ，原型链中也不存在此方法

    }
</script>

<body></body>

</html>